<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
	<title>join(联结)数据类型 | ElasticSearch 7.7 权威指南中文版</title>
	<meta name="keywords" content="ElasticSearch 权威指南中文版, elasticsearch 7, es7, 实时数据分析，实时数据检索" />
    <meta name="description" content="ElasticSearch 权威指南中文版, elasticsearch 7, es7, 实时数据分析，实时数据检索" />
    <!-- Give IE8 a fighting chance -->
    <!--[if lt IE 9]>
    <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
    <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
	<link rel="stylesheet" type="text/css" href="../static/styles.css" />
	<script>
	var _link = 'parent-join.html';
    </script>
</head>
<body>
<div class="main-container">
    <section id="content">
        <div class="content-wrapper">
            <section id="guide" lang="zh_cn">
                <div class="container">
                    <div class="row">
                        <div class="col-xs-12 col-sm-8 col-md-8 guide-section">
                            <div style="color:gray; word-break: break-all; font-size:12px;">原英文版地址: <a href="https://www.elastic.co/guide/en/elasticsearch/reference/7.7/parent-join.html" rel="nofollow" target="_blank">https://www.elastic.co/guide/en/elasticsearch/reference/7.7/parent-join.html</a>, 原文档版权归 www.elastic.co 所有<br/>本地英文版地址: <a href="../en/parent-join.html" rel="nofollow" target="_blank">../en/parent-join.html</a></div>
                        <!-- start body -->
                  <div class="page_header">
<strong>重要</strong>: 此版本不会发布额外的bug修复或文档更新。最新信息请参考 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html" rel="nofollow">当前版本文档</a>。
</div>
<div id="content">
<div class="breadcrumbs">
<span class="breadcrumb-link"><a href="index.html">Elasticsearch 权威指南 [7.7]</a></span>
»
<span class="breadcrumb-link"><a href="mapping.html">映射</a></span>
»
<span class="breadcrumb-link"><a href="mapping-types.html">字段数据类型</a></span>
»
<span class="breadcrumb-node">join(联结)数据类型</span>
</div>
<div class="navheader">
<span class="prev">
<a href="ip.html">« IP 数据类型</a>
</span>
<span class="next">
<a href="keyword.html">keyword(关键词)数据类型 »</a>
</span>
</div>
<div class="section">
<div class="titlepage"><div><div>
<h2 class="title">
<a id="parent-join"></a>join(联结)数据类型
</h2>
</div></div></div>

<p>
<code class="literal">join</code>数据类型是一种特殊的字段，它在同一个索引的文档中创建父-子关系。

<code class="literal">relations</code>部分定义了文档中一组可能的关系，每个关系都是一个父名称和一个子名称。

父-子关系可以定义如下：
</p>
<div class="pre_wrapper lang-console">
<pre class="programlisting prettyprint lang-console">PUT /my_index
{
  "mappings": {
    "properties": {
      "my_id": {
        "type": "keyword"
      },
      "my_join_field": { <a id="CO297-1"></a><i class="conum" data-value="1"></i>
        "type": "join",
        "relations": {
          "question": "answer" <a id="CO297-2"></a><i class="conum" data-value="2"></i>
        }
      }
    }
  }
}</pre>
</div>
<div class="console_widget" data-snippet="snippets/659.console"></div>
<div class="calloutlist">
<table border="0" summary="Callout list">
<tr>
<td align="left" valign="top" width="5%">
<p><a href="#CO297-1"><i class="conum" data-value="1"></i></a></p>
</td>
<td align="left" valign="top">
<p>字段名称</p>
</td>
</tr>
<tr>
<td align="left" valign="top" width="5%">
<p><a href="#CO297-2"><i class="conum" data-value="2"></i></a></p>
</td>
<td align="left" valign="top">
<p>定义<code class="literal">question</code>是<code class="literal">answer</code>的父项的单一关系。</p>
</td>
</tr>
</table>
</div>
<p>
要使用 join 对文档进行索引，必须在<code class="literal">source</code>中提供关系的名称和文档的可选父文档。

例如，下面这个例子在<code class="literal">question</code>上下文中创建了两个 <code class="literal">parent</code> 文档：
</p>
<div class="pre_wrapper lang-console">
<pre class="programlisting prettyprint lang-console">PUT /my_index/_doc/1?refresh
{
  "my_id": "1",
  "text": "This is a question",
  "my_join_field": {
    "name": "question" <a id="CO298-1"></a><i class="conum" data-value="1"></i>
  }
}

PUT /my_index/_doc/2?refresh
{
  "my_id": "2",
  "text": "This is another question",
  "my_join_field": {
    "name": "question"
  }
}</pre>
</div>
<div class="console_widget" data-snippet="snippets/660.console"></div>
<div class="calloutlist">
<table border="0" summary="Callout list">
<tr>
<td align="left" valign="top" width="5%">
<p><a href="#CO298-1"><i class="conum" data-value="1"></i></a></p>
</td>
<td align="left" valign="top">
<p>该文档是一个<code class="literal">question</code>文档。</p>
</td>
</tr>
</table>
</div>
<p>在给父文档编制索引时，可以选择仅指定关系的名称作为快捷方式，而不是将其封装在普通对象符号中：</p>
<div class="pre_wrapper lang-console">
<pre class="programlisting prettyprint lang-console">PUT my_index/_doc/1?refresh
{
  "my_id": "1",
  "text": "This is a question",
  "my_join_field": "question" <a id="CO299-1"></a><i class="conum" data-value="1"></i>
}

PUT my_index/_doc/2?refresh
{
  "my_id": "2",
  "text": "This is another question",
  "my_join_field": "question"
}</pre>
</div>
<div class="console_widget" data-snippet="snippets/661.console"></div>
<div class="calloutlist">
<table border="0" summary="Callout list">
<tr>
<td align="left" valign="top" width="5%">
<p><a href="#CO299-1"><i class="conum" data-value="1"></i></a></p>
</td>
<td align="left" valign="top">
<p>更简单的父文档符号只使用关系名。</p>
</td>
</tr>
</table>
</div>
<p>
当为子文档编制索引时，必须在<code class="literal">_source</code>中添加关系的名称以及文档的父id。
</p>
<div class="warning admon">
<div class="icon"></div>
<div class="admon_content">
<p>需要在同一个分片中索引父文档的传承关系，因此必须始终使用更大的父id来路由子文档。</p>
</div>
</div>
<p>例如，下面这个例子显示了如何索引两个 <code class="literal">child</code> 文档：</p>
<div class="pre_wrapper lang-console">
<pre class="programlisting prettyprint lang-console">PUT my_index/_doc/3?routing=1&amp;refresh <a id="CO300-1"></a><i class="conum" data-value="1"></i>
{
  "my_id": "3",
  "text": "This is an answer",
  "my_join_field": {
    "name": "answer", <a id="CO300-2"></a><i class="conum" data-value="2"></i>
    "parent": "1" <a id="CO300-3"></a><i class="conum" data-value="3"></i>
  }
}

PUT my_index/_doc/4?routing=1&amp;refresh
{
  "my_id": "4",
  "text": "This is another answer",
  "my_join_field": {
    "name": "answer",
    "parent": "1"
  }
}</pre>
</div>
<div class="console_widget" data-snippet="snippets/662.console"></div>
<div class="calloutlist">
<table border="0" summary="Callout list">
<tr>
<td align="left" valign="top" width="5%">
<p><a href="#CO300-1"><i class="conum" data-value="1"></i></a></p>
</td>
<td align="left" valign="top">
<p>路由值是必需的，因为父文档和子文档必须在同一个分片上建立索引</p>
</td>
</tr>
<tr>
<td align="left" valign="top" width="5%">
<p><a href="#CO300-2"><i class="conum" data-value="2"></i></a></p>
</td>
<td align="left" valign="top">
<p><code class="literal">answer</code>是该文档的联结(join)的名字</p>
</td>
</tr>
<tr>
<td align="left" valign="top" width="5%">
<p><a href="#CO300-3"><i class="conum" data-value="3"></i></a></p>
</td>
<td align="left" valign="top">
<p>子文档的父id</p>
</td>
</tr>
</table>
</div>
<div class="section">
<div class="titlepage"><div><div>
<h3 class="title">
<a id="_parent_join_and_performance"></a>父联结及性能
</h3>
</div></div></div>
<p>
join 字段不应该像关系型数据库中的 JOIN 一样使用。

在Elasticsearch中，良好性能的关键是将数据反归一化到文档中。

每个 join 字段、<code class="literal">has_child</code>或<code class="literal">has_parent</code>查询都会显著提高查询性能。
</p>
<p>
join 字段有意义的唯一情况是数据包含一对多关系，其中一个实体的数量远远超过另一个实体。

这种情况的一个例子是产品及其优惠信息的用例。

在优惠信息明显多于产品数量的情况下，将产品建模为父文档，将优惠建模为子文档是有意义的。
</p>
</div>

<div class="section">
<div class="titlepage"><div><div>
<h3 class="title">
<a id="_parent_join_restrictions"></a>父联结的限制
</h3>
</div></div></div>
<div class="ulist itemizedlist">
<ul class="itemizedlist">
<li class="listitem">
每个索引中只能有一个<code class="literal">join</code>字段映射。
</li>
<li class="listitem">
父文档和子文档必须被索引在同一个分片上。

这意味着在<a class="xref" href="docs-get.html" title="get API" rel="nofollow">获取</a>、<a class="xref" href="docs-delete.html" title="delete API" rel="nofollow">删除</a>或<a class="xref" href="docs-update.html" title="update API" rel="nofollow">更新</a>子文档时，需要提供相同的 <code class="literal">routing</code>(路由) 值。
</li>
<li class="listitem">
一个元素可以有多个子元素，但只能有一个父元素。
</li>
<li class="listitem">
可以向现有的<code class="literal">join</code>字段添加新的关系。
</li>
<li class="listitem">
也可以向现有元素添加子元素，但前提是该元素已经是父元素。
</li>
</ul>
</div>
</div>

<div class="section">
<div class="titlepage"><div><div>
<h3 class="title">
<a id="_searching_with_parent_join"></a>使用父联结(parent join)进行搜索
</h3>
</div></div></div>
<p>
父联结(parent join)创建一个字段来索引文档中关系的名称(<code class="literal">my_parent</code>, <code class="literal">my_child</code>，...)。
</p>
<p>
它还为每个父-子关系创建一个字段。

该字段的名称是<code class="literal">join</code>字段的名称，后跟<code class="literal">#</code>及关系中父字段的名称。

例如，对于<code class="literal">my_parent</code> → [<code class="literal">my_child</code>, <code class="literal">another_child</code>]关系，<code class="literal">join</code>字段会创建一个名为<code class="literal">my_join_field#my_parent</code>的附加字段。
</p>
<p>
如果该文档是一个子文档(<code class="literal">my_child</code> or <code class="literal">another_child</code>)，则此字段包含文档链接到的父文档的<code class="literal">_id</code>，如果它是一个父文档(<code class="literal">my_parent</code>)，则包含文档的<code class="literal">_id</code>。
</p>
<p>当搜索一个包含<code class="literal">join</code>字段的索引时，搜索响应中总是返回这两个字段：</p>
<div class="pre_wrapper lang-console">
<pre class="programlisting prettyprint lang-console">GET my_index/_search
{
  "query": {
    "match_all": {}
  },
  "sort": ["my_id"]
}</pre>
</div>
<div class="console_widget" data-snippet="snippets/663.console"></div>
<p>它将返回：</p>
<div class="pre_wrapper lang-console-result">
<pre class="programlisting prettyprint lang-console-result">{
    ...,
    "hits": {
        "total" : {
            "value": 4,
            "relation": "eq"
        },
        "max_score": null,
        "hits": [
            {
                "_index": "my_index",
                "_type": "_doc",
                "_id": "1",
                "_score": null,
                "_source": {
                    "my_id": "1",
                    "text": "This is a question",
                    "my_join_field": "question" <a id="CO301-1"></a><i class="conum" data-value="1"></i>
                },
                "sort": [
                    "1"
                ]
            },
            {
                "_index": "my_index",
                "_type": "_doc",
                "_id": "2",
                "_score": null,
                "_source": {
                    "my_id": "2",
                    "text": "This is another question",
                    "my_join_field": "question" <a id="CO301-2"></a><i class="conum" data-value="2"></i>
                },
                "sort": [
                    "2"
                ]
            },
            {
                "_index": "my_index",
                "_type": "_doc",
                "_id": "3",
                "_score": null,
                "_routing": "1",
                "_source": {
                    "my_id": "3",
                    "text": "This is an answer",
                    "my_join_field": {
                        "name": "answer", <a id="CO301-3"></a><i class="conum" data-value="3"></i>
                        "parent": "1"  <a id="CO301-4"></a><i class="conum" data-value="4"></i>
                    }
                },
                "sort": [
                    "3"
                ]
            },
            {
                "_index": "my_index",
                "_type": "_doc",
                "_id": "4",
                "_score": null,
                "_routing": "1",
                "_source": {
                    "my_id": "4",
                    "text": "This is another answer",
                    "my_join_field": {
                        "name": "answer",
                        "parent": "1"
                    }
                },
                "sort": [
                    "4"
                ]
            }
        ]
    }
}</pre>
</div>
<div class="calloutlist">
<table border="0" summary="Callout list">
<tr>
<td align="left" valign="top" width="5%">
<p><a href="#CO301-1"><i class="conum" data-value="1"></i></a></p>
</td>
<td align="left" valign="top">
<p>该文档属于<code class="literal">question</code>联结</p>
</td>
</tr>
<tr>
<td align="left" valign="top" width="5%">
<p><a href="#CO301-2"><i class="conum" data-value="2"></i></a></p>
</td>
<td align="left" valign="top">
<p>该文档属于<code class="literal">question</code>联结</p>
</td>
</tr>
<tr>
<td align="left" valign="top" width="5%">
<p><a href="#CO301-3"><i class="conum" data-value="3"></i></a></p>
</td>
<td align="left" valign="top">
<p>该文档属于<code class="literal">answer</code>联结</p>
</td>
</tr>
<tr>
<td align="left" valign="top" width="5%">
<p><a href="#CO301-4"><i class="conum" data-value="4"></i></a></p>
</td>
<td align="left" valign="top">
<p>与子文档关联的父id</p>
</td>
</tr>
</table>
</div>
</div>

<div class="section">
<div class="titlepage"><div><div>
<h3 class="title">
<a id="_parent_join_queries_and_aggregations"></a>父联结查询及聚合
</h3>
</div></div></div>
<p>
更多信息，请参考<a class="xref" href="query-dsl-has-child-query.html" title="has_child查询"><code class="literal">has_child</code></a>和<a class="xref" href="query-dsl-has-parent-query.html" title="has_parent查询"><code class="literal">has_parent</code></a>查询，<a class="xref" href="search-aggregations-bucket-children-aggregation.html" title="children聚合"><code class="literal">children</code></a>聚合及<a class="xref" href="search-request-body.html#parent-child-inner-hits" title="parent/child内部命中" rel="nofollow">内部命中(inner hits)</a>。
</p>
<p>
可以在聚合和脚本中访问<code class="literal">join</code>字段的值，并且可以使用<a class="xref" href="query-dsl-parent-id-query.html" title="parent_id查询"><code class="literal">parent_id</code>查询</a>进行查询：
</p>
<div class="pre_wrapper lang-console">
<pre class="programlisting prettyprint lang-console">GET my_index/_search
{
  "query": {
    "parent_id": { <a id="CO302-1"></a><i class="conum" data-value="1"></i>
      "type": "answer",
      "id": "1"
    }
  },
  "aggs": {
    "parents": {
      "terms": {
        "field": "my_join_field#question", <a id="CO302-2"></a><i class="conum" data-value="2"></i>
        "size": 10
      }
    }
  },
  "script_fields": {
    "parent": {
      "script": {
         "source": "doc['my_join_field#question']" <a id="CO302-3"></a><i class="conum" data-value="3"></i>
      }
    }
  }
}</pre>
</div>
<div class="console_widget" data-snippet="snippets/664.console"></div>
<div class="calloutlist">
<table border="0" summary="Callout list">
<tr>
<td align="left" valign="top" width="5%">
<p><a href="#CO302-1"><i class="conum" data-value="1"></i></a></p>
</td>
<td align="left" valign="top">
<p>
查询<code class="literal">parent id</code>字段(另请参见<a class="xref" href="query-dsl-has-parent-query.html" title="has_parent查询"><code class="literal">has_parent</code>查询</a>和<a class="xref" href="query-dsl-has-child-query.html" title="has_child查询"><code class="literal">has_child</code>查询</a>)
</p>
</td>
</tr>
<tr>
<td align="left" valign="top" width="5%">
<p><a href="#CO302-2"><i class="conum" data-value="2"></i></a></p>
</td>
<td align="left" valign="top">
<p>
在<code class="literal">parent id</code>字段上聚合(另请参见<a class="xref" href="search-aggregations-bucket-children-aggregation.html" title="children聚合"><code class="literal">children</code></a>聚合)
</p>
</td>
</tr>
<tr>
<td align="left" valign="top" width="5%">
<p><a href="#CO302-3"><i class="conum" data-value="3"></i></a></p>
</td>
<td align="left" valign="top">
<p>在脚本中访问父对象的id字段</p>
</td>
</tr>
</table>
</div>
</div>

<div class="section">
<div class="titlepage"><div><div>
<h3 class="title">
<a id="_global_ordinals"></a>全局序号(global ordinals)
</h3>
</div></div></div>
<p>
字段使用<code class="literal">join</code> field uses <a class="xref" href="eager-global-ordinals.html" title="eager_global_ordinals" rel="nofollow">全局序号(global ordinals)</a>来加速联结。

在对分片进行任何更改后，都需要重新构建全局序号。

一个分片中存储的父 id 值越多，为<code class="literal">join</code>字段重新构建全局序号所需的时间就越长。
</p>
<p>
默认情况下，会急切地构建全局序号：如果索引发生了变化，那么<code class="literal">join</code>字段的全局序号将作为 refresh 的一部分重新构建。

这可能会大大增加 refresh 的时间。

然而，大多数情况下这是正确的权衡，否则当使用第一个 父联结(parent-join) 查询或聚合时，将重新构建全局序号。

这可能会给你的用户带来显著的延迟峰值，通常情况下，当发生多次写入时，可能会在单个 refresh 间隔内尝试重建<code class="literal">join</code>字段的多个全局序号，这种情况会更糟。
</p>
<p>
当<code class="literal">join</code>字段很少使用但频繁写入时，禁用 快速加载(eager loading) 可能是有意义的：
</p>
<div class="pre_wrapper lang-console">
<pre class="programlisting prettyprint lang-console">PUT my_index
{
  "mappings": {
    "properties": {
      "my_join_field": {
        "type": "join",
        "relations": {
           "question": "answer"
        },
        "eager_global_ordinals": false
      }
    }
  }
}</pre>
</div>
<div class="console_widget" data-snippet="snippets/665.console"></div>
<p>
可以按如下方式检查每个父关系的全局序号使用的 heap 的大小：
</p>
<div class="pre_wrapper lang-console">
<pre class="programlisting prettyprint lang-console"># Per-index
GET _stats/fielddata?human&amp;fields=my_join_field#question

# Per-node per-index
GET _nodes/stats/indices/fielddata?human&amp;fields=my_join_field#question</pre>
</div>
<div class="console_widget" data-snippet="snippets/666.console"></div>
</div>

<div class="section">
<div class="titlepage"><div><div>
<h3 class="title">
<a id="_multiple_children_per_parent"></a>一父多子
</h3>
</div></div></div>
<p>也可以为单个父对象定义多个子对象：</p>
<div class="pre_wrapper lang-console">
<pre class="programlisting prettyprint lang-console">PUT my_index
{
  "mappings": {
    "properties": {
      "my_join_field": {
        "type": "join",
        "relations": {
          "question": ["answer", "comment"]  <a id="CO303-1"></a><i class="conum" data-value="1"></i>
        }
      }
    }
  }
}</pre>
</div>
<div class="console_widget" data-snippet="snippets/667.console"></div>
<div class="calloutlist">
<table border="0" summary="Callout list">
<tr>
<td align="left" valign="top" width="5%">
<p><a href="#CO303-1"><i class="conum" data-value="1"></i></a></p>
</td>
<td align="left" valign="top">
<p><code class="literal">question</code>是<code class="literal">answer</code>和<code class="literal">comment</code>的父对象。</p>
</td>
</tr>
</table>
</div>
</div>

<div class="section">
<div class="titlepage"><div><div>
<h3 class="title">
<a id="_multiple_levels_of_parent_join"></a>多层级父联结
</h3>
</div></div></div>
<div class="warning admon">
<div class="icon"></div>
<div class="admon_content">
<p>
不建议使用多层关系来复制关系模型。

每一级关系都会在查询时增加内存和计算方面的开销。

如果你关心性能，应该对数据进行反归一化。
</p>
</div>
</div>
<p>多层级的父-子关系：</p>
<div class="pre_wrapper lang-console">
<pre class="programlisting prettyprint lang-console">PUT my_index
{
  "mappings": {
    "properties": {
      "my_join_field": {
        "type": "join",
        "relations": {
          "question": ["answer", "comment"],  <a id="CO304-1"></a><i class="conum" data-value="1"></i>
          "answer": "vote" <a id="CO304-2"></a><i class="conum" data-value="2"></i>
        }
      }
    }
  }
}</pre>
</div>
<div class="console_widget" data-snippet="snippets/668.console"></div>
<div class="calloutlist">
<table border="0" summary="Callout list">
<tr>
<td align="left" valign="top" width="5%">
<p><a href="#CO304-1"><i class="conum" data-value="1"></i></a></p>
</td>
<td align="left" valign="top">
<p><code class="literal">question</code>是<code class="literal">answer</code>和<code class="literal">comment</code>的父对象</p>
</td>
</tr>
<tr>
<td align="left" valign="top" width="5%">
<p><a href="#CO304-2"><i class="conum" data-value="2"></i></a></p>
</td>
<td align="left" valign="top">
<p><code class="literal">answer</code>是<code class="literal">vote</code>的父级</p>
</td>
</tr>
</table>
</div>
<p>上面的映射表示下面的树状结构：</p>
<pre class="literallayout">   question
    /    \
   /      \
comment  answer
           |
           |
          vote</pre>

<p>
为孙子文档编制索引需要<code class="literal">routing</code>值等于曾祖父(曾祖父的传承)：
</p>
<div class="pre_wrapper lang-console">
<pre class="programlisting prettyprint lang-console">PUT my_index/_doc/3?routing=1&amp;refresh <a id="CO305-1"></a><i class="conum" data-value="1"></i>
{
  "text": "This is a vote",
  "my_join_field": {
    "name": "vote",
    "parent": "2" <a id="CO305-2"></a><i class="conum" data-value="2"></i>
  }
}</pre>
</div>
<div class="console_widget" data-snippet="snippets/669.console"></div>
<div class="calloutlist">
<table border="0" summary="Callout list">
<tr>
<td align="left" valign="top" width="5%">
<p><a href="#CO305-1"><i class="conum" data-value="1"></i></a></p>
</td>
<td align="left" valign="top">
<p>子文档必须与其祖父文档和父文档在同一个分片上</p>
</td>
</tr>
<tr>
<td align="left" valign="top" width="5%">
<p><a href="#CO305-2"><i class="conum" data-value="2"></i></a></p>
</td>
<td align="left" valign="top">
<p>此文档的父id(必须指向一个<code class="literal">answer</code>文档)</p>
</td>
</tr>
</table>
</div>
</div>

</div>
<div class="navfooter">
<span class="prev">
<a href="ip.html">« IP 数据类型</a>
</span>
<span class="next">
<a href="keyword.html">keyword(关键词)数据类型 »</a>
</span>
</div>
</div>

                  <!-- end body -->
                        </div>
                        <div class="col-xs-12 col-sm-4 col-md-4" id="right_col">
                        
                        </div>
                    </div>
                </div>
            </section>
        </div>
    </section>
</div>
<script src="../static/cn.js"></script>
</body>
</html>